<?php

namespace app\admin\model\examples\joyspec;

use ba\Exception;
use think\helper\Arr;
use think\Model;
use think\model\relation\HasMany;
use Throwable;

/**
 * Goods
 */
class Goods extends Model
{
    // 表名
    protected $name = 'joy_spec_goods';

    // 自动写入时间戳字段
    protected $autoWriteTimestamp = true;
    
    protected $json = ['spec_list'];
    
    protected $jsonAssoc = true;
    
    protected $append = ['spec_data'];
    
    public function sku(): HasMany
    {
        return $this->hasMany(GoodsSku::class, 'goods_id', 'id');
    }
    
    public function getSpecDataAttr(): array
    {
        $skus = $this->sku->each(function ($sku) {
            $sku->price        = floatval($sku->price);
            $sku->origin_price = floatval($sku->origin_price);
        });
        
        return [
            'specList' => $this->getData('spec_list'),
            'skuList'  => $skus,
        ];
    }
    
    /**
     * 插入前事件
     * @param Model $model
     */
    public static function onBeforeWrite(Model $model): void
    {
        $specList = request()->post('spec_data')['specList'] ?? [];
        
        $model->spec_list = json_encode($specList, JSON_UNESCAPED_UNICODE);
    }
    
    /**
     * 写入后事件
     * @param Model $model
     * @throws Exception
     */
    public static function onAfterWrite(Model $model): void
    {
        $specData = request()->post('spec_data');
        
        (new self)->handleSpecData($model->getData('id'), $specData);
    }
    
    public static function onAfterDelete(Model $model): void
    {
        $model->sku()->delete();
    }
    
    /**
     * 处理规格数据
     * @param int $goodsId
     * @param array $specData
     * @return void
     * @throws Exception
     */
    public function handleSpecData(int $goodsId, array $specData): void
    {
        $specList = $specData['specList'] ?? [];
        $skuList  = $specData['skuList'] ?? [];
        if (!$specList || !$skuList) {
            throw new Exception(__('Incomplete specification data'));
        }
        
        //兼容单规格属性值
        array_map(fn(&$attr) => $attr['value'] = (array)$attr['value'], $specList);
        
        $this->validateSpecData($specList, $skuList);
        
        try {
            $this->handleSkuInfo($goodsId, $skuList);
        } catch (Throwable $e) {
            throw new Exception($e->getMessage());
        }
    }
    
    /**
     * 校验规格数据
     * @param array $specList
     * @param array $skuList
     * @return void
     * @throws Exception
     */
    private function validateSpecData(array $specList, array $skuList): void
    {
        $attrNames = array_column($specList, 'name');
        if (count($attrNames) !== count(array_unique($attrNames))) {
            throw new Exception(__('The specification name cannot be duplicated'));
        }
        
        $attrValues = array_column($specList, 'value');
        
        $attrValues = array_reduce($attrValues, 'array_merge', []);
        
        if (count($attrValues) !== count(array_unique($attrValues))) {
            throw new Exception(__('The specification value cannot be repeated'));
        }
        
        $generateSpecs = $this->generateProductSpecs($specList);
        $specs         = array_column($skuList, 'spec');
        foreach ($specs as $spec) {
            if (!in_array($spec, $generateSpecs)) {
                throw new Exception(__('The specification attributes do not match the generated SKU'));
            }
        }
    }
    
    /**
     * 生成商品规格
     * @param array $specList
     * @return array
     */
    private function generateProductSpecs(array $specList): array
    {
        $result = [[]];
        
        foreach ($specList as $spec) {
            $tmp = [];
            foreach ($result as $res) {
                foreach ($spec['value'] as $value) {
                    $tmp[] = array_merge($res, [$spec['name'] . ':' . $value]);
                }
            }
            $result = $tmp;
        }
        
        return array_map(function ($item) {
            return implode(';', $item);
        }, $result);
    }
    
    /**
     * 处理规格SKU信息
     * @param int $goodsId 商品ID
     * @param array $skuList 规格列表
     */
    private function handleSkuInfo(int $goodsId, array $skuList): void
    {
        $existIds = [];
        foreach ($skuList as $sku) {
            if (Arr::has($sku, 'id')) {
                GoodsSku::update($sku, ['id' => $sku['id']]);
                $existIds[] = $sku['id'];
            } else {
                $spec       = GoodsSku::create(array_merge($sku, ['goods_id' => $goodsId, 'spec' => $sku['spec']]));
                $existIds[] = $spec->id;
            }
        }
        
        GoodsSku::destroy(function ($query) use ($goodsId, $existIds) {
            $query->where('goods_id', $goodsId)->whereNotIn('id', $existIds);
        });
    }
}